package sf.r2dbc.sql;

import io.r2dbc.spi.Connection;
import reactor.core.publisher.Mono;
import sf.core.DBField;
import sf.core.DBObject;
import sf.database.dao.DBContext;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.SQLContext;
import sf.database.jdbc.sql.SQLParameter;
import sf.database.meta.ColumnMapping;
import sf.database.meta.MetaHolder;
import sf.database.meta.TableMapping;
import sf.database.util.OrmUtils;
import sf.database.util.OrmValueUtils;
import sf.spring.util.CollectionUtils;
import sf.tools.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 乐观锁支持
 * @see javax.persistence.Version
 */
public class R2dbcOptimisticLock {
    /**
     * @param connection
     * @param obj
     * @param <T>
     * @return
     * @see javax.persistence.Version
     * 从数据库中获取最新的乐观锁的值到对象中,一般情况下,该方法用不到.慎用.
     */
    public static <T extends DBObject> Mono<Void> setNewOptimisticLockValues(Connection connection, T obj) {
        TableMapping table = MetaHolder.getMeta(obj.getClass());
        DBDialect dialect = R2dbcUtils.getDBDialect(connection);
        DBContext dbContext = R2dbcUtils.getDBContext(connection);
        List<String> fStr = new ArrayList<>();
        if (!CollectionUtils.isEmpty(table.getVersionMap())) {
            for (Map.Entry<DBField, ColumnMapping> e : table.getVersionMap().entrySet()) {
                ColumnMapping cm = e.getValue();
                String columnName = cm.getRawColumnName();
                fStr.add(dialect.wrapKeyword(columnName));
            }
            String tableName = dialect.wrapKeyword(OrmUtils.getDynamicTableName(dbContext, table));
            SQLContext c = new SQLContext(table);
            List<ColumnMapping> pKeys = table.getPkFields();
            List<SQLParameter> parameters = new ArrayList<>();
            StringBuilder sql = new StringBuilder();
            sql.append("select ").append(StringUtils.join(fStr, ",")).append(" from ").append(tableName);
            boolean f = false;
            for (ColumnMapping pk : pKeys) {
                sql.append(f ? " and " : " where ").append(dialect.wrapKeyword(pk.getRawColumnName())).append(" =? ");
                Object value = OrmValueUtils.getValue(obj, pk);
                parameters.add(new SQLParameter(value, pk));
                f = true;
            }
            c.setSql(sql.toString());
            c.setParas(parameters);

            return R2dbcCrud.getInstance().getCrudModel().selectOne(connection, obj.getClass(), c).flatMap(temp -> {
                if (temp != null) {
                    //设置最新的版本值到obj对象中
                    for (Map.Entry<DBField, ColumnMapping> e : table.getVersionMap().entrySet()) {
                        ColumnMapping cm = e.getValue();
                        Object value = OrmValueUtils.getValue(temp, cm);
                        OrmValueUtils.setValue(obj, cm, value);
                    }
                }
                return Mono.empty();
            });
        }
        return Mono.empty();
    }
}
